<FrameworkSwitchCourse {fw} />

# Traducere[[translation]]

{#if fw === 'pt'}

<CourseFloatingBanner chapter={7}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter7/section4_pt.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section4_pt.ipynb"},
]} />

{:else}

<CourseFloatingBanner chapter={7}
  classNames="absolute z-10 right-0 top-0"
  notebooks={[
    {label: "Google Colab", value: "https://colab.research.google.com/github/huggingface/notebooks/blob/master/course/en/chapter7/section4_tf.ipynb"},
    {label: "Aws Studio", value: "https://studiolab.sagemaker.aws/import/github/huggingface/notebooks/blob/master/course/en/chapter7/section4_tf.ipynb"},
]} />

{/if}

Să trecem acum la traducere. Aceasta este o altă [sarcină de sequence-to-sequence](/course/chapter1/7), ceea ce înseamnă că este o problemă care poate fi formulată ca trecerea de la o secvență la alta. În acest sens, problema este destul de apropiată de [sumarizare](/course/chapter7/6) și ați putea adapta ceea ce vom vedea aici la alte probleme de la sequence-to-sequence, cum ar fi:

- **Style transfer**: Crearea unui model care *traduce* texte scrise într-un anumit stil în altul (de exemplu, din formal în casual sau din engleza Shakespeariană în engleza modernă)
- **Generative question answering**: Crearea unui model care generează răspunsuri la întrebări, fiind dat un context

<Youtube id="1JvfrvZgi6c"/>

Dacă aveți un corpus suficient de mare de texte în două (sau mai multe) limbi, puteți antrena un nou model de traducere de la zero, așa cum vom face în secțiunea despre [causal language modeling] (/course/chapter7/6). Cu toate acestea, va fi mai rapid să faceți fine-tune unui model de traducere existent, fie că este vorba de un model multilingv precum mT5 sau mBART, pe care doriți să îi faceți fine-tune pentru o anumită pereche de limbi, sau chiar un model specializat pentru traducerea dintr-o limbă în alta, pe care doriți să îl perfecționați pentru corpusul vostru specific.

În această secțiune, vom pune face fine-tune unui model Marian preantrenat pentru a traduce din engleză în franceză (deoarece mulți dintre angajații Hugging Face vorbesc ambele limbi) pe [datasetul KDE4](https://huggingface.co/datasets/kde4), care este un set de fișiere localizate pentru [KDE apps](https://apps.kde.org/). Modelul pe care îl vom utiliza a fost preantrenat pe un corpus mare de texte în franceză și engleză preluate din [datasetul Opus](https://opus.nlpl.eu/), care conține de fapt datasetul KDE4. Dar chiar dacă modelul preantrenat pe care îl folosim a văzut aceste date în timpul preantrenării sale, vom vedea că putem obține o versiune mai bună a acestuia după fine-tuning.

Odată ce am terminat, vom avea un model capabil să facă predicții ca acesta:

<iframe src="https://course-demos-marian-finetuned-kde4-en-to-fr.hf.space" frameBorder="0" height="350" title="Gradio app" class="block dark:hidden container p-0 flex-grow space-iframe" allow="accelerometer; ambient-light-sensor; autoplay; battery; camera; document-domain; encrypted-media; fullscreen; geolocation; gyroscope; layout-animations; legacy-image-formats; magnetometer; microphone; midi; oversized-images; payment; picture-in-picture; publickey-credentials-get; sync-xhr; usb; vr ; wake-lock; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts allow-downloads"></iframe>

<a class="flex justify-center" href="/huggingface-course/marian-finetuned-kde4-en-to-fr">
<img class="block dark:hidden lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/modeleval-marian-finetuned-kde4-en-to-fr.png" alt="One-hot encoded labels pentru răspunderea la întrebări."/>
<img class="hidden dark:block lg:w-3/5" src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/modeleval-marian-finetuned-kde4-en-to-fr-dark.png" alt="One-hot encoded labels răspunderea la întrebări."/>
</a>

Ca și în secțiunile anterioare, puteți găsi modelul pe care îl vom antrena și încărca în Hub utilizând codul de mai jos și puteți verifica predicțiile acestuia [aici](https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr?text=This+plugin+allows+you+to+automatically+translate+web+pages+between+several+languages.).

## Pregătirea datelor[[preparing-the-data]]

Pentru a face fine-tune sau a antrena un model de traducere de la zero, vom avea nevoie de un dataset adecvat pentru această sarcină. După cum am menționat anterior, vom utiliza [datasetul KDE4](https://huggingface.co/datasets/kde4) în această secțiune, dar puteți adapta codul pentru a utiliza propriile date destul de ușor, atâta timp cât aveți perechi de propoziții în cele două limbi din și în care doriți să traduceți. Consultați [Capitolul 5](/course/chapter5) dacă aveți nevoie de o reamintire a modului de încărcare a datelor personalizate într-un `Dataset`.

### Datasetul KDE4[[the-kde4-dataset]]

Ca de obicei, descărcăm datasetul nostru folosind funcția `load_dataset()`:

```py
from datasets import load_dataset

raw_datasets = load_dataset("kde4", lang1="en", lang2="fr")
```

Dacă doriți să lucrați cu o pereche de limbi diferită, le puteți specifica prin codurile lor. Un total de 92 de limbi sunt disponibile pentru acest dataset; le puteți vedea pe toate prin extinderea labelurilor de limbă pe [dataset cardul acesteia](https://huggingface.co/datasets/kde4).

<img src="https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/en/chapter7/language_tags.png" alt="Limba disponibilă pentru datasetul KDE4." width="100%">

Să aruncăm o privire la dataset:

```py
raw_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'translation'],
        num_rows: 210173
    })
})
```

Avem 210 173 de perechi de propoziții, dar într-o singură împărțire, deci va trebui să ne creăm propriul set de validare. Așa cum am văzut în [Capitolul 5](/course/chapter5), un `Dataset` are o metodă `train_test_split()` care ne poate ajuta. Vom furniza un seed pentru reproductibilitate:

```py
split_datasets = raw_datasets["train"].train_test_split(train_size=0.9, seed=20)
split_datasets
```

```python out
DatasetDict({
    train: Dataset({
        features: ['id', 'translation'],
        num_rows: 189155
    })
    test: Dataset({
        features: ['id', 'translation'],
        num_rows: 21018
    })
})
```

Putem redenumi cheia `"test"` în `"validation"` astfel:

```py
split_datasets["validation"] = split_datasets.pop("test")
```

Acum să aruncăm o privire la un element al datasetului:

```py
split_datasets["train"][1]["translation"]
```

```python out
{'en': 'Default to expanded threads',
 'fr': 'Par défaut, développer les fils de discussion'}
```

Obținem un dicționar cu două propoziții în perechea de limbi solicitate. O particularitate a acestui dataset plin de termeni tehnici din domeniul informaticii este că toate sunt traduse integral în franceză. Cu toate acestea, inginerii francezi lasă majoritatea cuvintelor specifice informaticii în engleză atunci când vorbesc. Aici, de exemplu, cuvântul "threads" ar putea foarte bine să apară într-o propoziție în limba franceză, în special într-o conversație tehnică; dar în acest dataset a fost tradus în "fils de discussion". Modelul preantrenat pe care îl folosim, care a fost preantrenat pe un corpus mai mare de propoziții în franceză și engleză, alege opțiunea mai ușoară de a lăsa cuvântul așa cum este:

```py
from transformers import pipeline

model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
```

```python out
[{'translation_text': 'Par défaut pour les threads élargis'}]
```

Un alt exemplu al acestui comportament poate fi văzut cu cuvântul "plugin", care nu este oficial un cuvânt francez, dar pe care majoritatea vorbitorilor nativi îl vor înțelege și nu se vor obosi să îl traducă.
În datasetul KDE4, acest cuvânt a fost tradus în franceză în termenul puțin mai oficial "module d'extension":

```py
split_datasets["train"][172]["translation"]
```

```python out
{'en': 'Unable to import %1 using the OFX importer plugin. This file is not the correct format.',
 'fr': "Impossible d'importer %1 en utilisant le module d'extension d'importation OFX. Ce fichier n'a pas un format correct."}
```

Modelul nostru preantrenat, cu toate acestea, rămâne la cuvântul englez compact și familiar:

```py
translator(
    "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
```

```python out
[{'translation_text': "Impossible d'importer %1 en utilisant le plugin d'importateur OFX. Ce fichier n'est pas le bon format."}]
```

Va fi interesant de văzut dacă modelul nostru fine-tuned reține aceste particularități ale datasetului(spoiler alert: va reține).

<Youtube id="0Oxphw4Q9fo"/>

> [!TIP]
> ✏️ **Rândul tău!** Un alt cuvânt englezesc care este adesea folosit în franceză este "email". Găsiți primul sample din datasetul de antrenare care utilizează acest cuvânt. Cum este tradus? Cum traduce modelul preantrenat aceeași propoziție în limba engleză?

### Procesarea datelor[[processing-the-data]]

<Youtube id="XAR8jnZZuUs"/>

Ar trebui să știți deja cum stă treaba: toate textele trebuie convertite în seturi de ID-uri token, astfel încât modelul să le poată înțelege. Pentru această sarcină, va trebui să tokenizăm atât inputurile, cât și targeturile. Prima noastră sarcină este să creăm obiectul `tokenizer`. După cum am menționat mai devreme, vom utiliza un model Marian preantrenat din engleză în franceză. Dacă încercați acest cod cu o altă pereche de limbi, asigurați-vă că adaptați checkpointul modelului. Organizația [Helsinki-NLP](https://huggingface.co/Helsinki-NLP) oferă mai mult de o mie de modele în mai multe limbi.

```python
from transformers import AutoTokenizer

model_checkpoint = "Helsinki-NLP/opus-mt-en-fr"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint, return_tensors="pt")
```

De asemenea, puteți înlocui `model_checkpoint` cu orice alt model preferat din [Hub](https://huggingface.co/models) sau cu un folder local în care ați salvat un model preantrenat și un tokenizer.

> [!TIP]
> 💡 Dacă utilizați un tokenizer multilingv, cum ar fi mBART, mBART-50 sau M2M100, va trebui să setați codurile de limbă ale inputurilor și targeturilor în tokenizer prin setarea `tokenizer.src_lang` și `tokenizer.tgt_lang` la valorile corecte.

Pregătirea datelor noastre este destul de simplă. Trebuie să rețineți un singur lucru; trebuie să vă asigurați că tokenizerul procesează targeturile în limba de ieșire (aici, franceză). Puteți face acest lucru trecând targeturile la argumentul `text_targets` al metodei `__call__` a tokenizerului.

Pentru a vedea cum funcționează acest lucru, să procesăm un sample din fiecare limbă din setul de antrenare:

```python
en_sentence = split_datasets["train"][1]["translation"]["en"]
fr_sentence = split_datasets["train"][1]["translation"]["fr"]

inputs = tokenizer(en_sentence, text_target=fr_sentence)
inputs
```

```python out
{'input_ids': [47591, 12, 9842, 19634, 9, 0], 'attention_mask': [1, 1, 1, 1, 1, 1], 'labels': [577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]}
```

După cum putem vedea, rezultatul conține ID-urile de input asociate cu propoziția în limba engleză, în timp ce ID-urile asociate cu cea în limba franceză sunt stocate în câmpul `labels`. Dacă uitați să indicați că tokenizați labelurile, acestea vor fi tokenizate de către tokenizerul de input, ceea ce în cazul unui model Marian nu va merge deloc bine:

```python
wrong_targets = tokenizer(fr_sentence)
print(tokenizer.convert_ids_to_tokens(wrong_targets["input_ids"]))
print(tokenizer.convert_ids_to_tokens(inputs["labels"]))
```

```python out
['▁Par', '▁dé', 'f', 'aut', ',', '▁dé', 've', 'lop', 'per', '▁les', '▁fil', 's', '▁de', '▁discussion', '</s>']
['▁Par', '▁défaut', ',', '▁développer', '▁les', '▁fils', '▁de', '▁discussion', '</s>']
```

După cum se poate observa, utilizarea tokenizerului englez pentru preprocesarea unei propoziții franceze are ca rezultat mult mai mulți tokeni, deoarece tokenizerul nu cunoaște niciun cuvânt francez (cu excepția celor care apar și în limba engleză, precum "discussion").

Deoarece `inputs` este un dicționar cu cheile noastre obișnuite (ID-uri de input, attention mask etc.), ultimul pas este să definim funcția de preprocesare pe care o vom aplica dataseturilor:

```python
max_length = 128


def preprocess_function(examples):
    inputs = [ex["en"] for ex in examples["translation"]]
    targets = [ex["fr"] for ex in examples["translation"]]
    model_inputs = tokenizer(
        inputs, text_target=targets, max_length=max_length, truncation=True
    )
    return model_inputs
```

Rețineți că am stabilit aceeași lungime maximă pentru inputurile și outputurile noastre. Deoarece textele cu care avem de-a face par destul de scurte, vom folosi 128.

> [!TIP]
> 💡 Dacă utilizați un model T5 (mai precis, unul dintre checkpointurile `t5-xxx`), modelul se va aștepta ca inputurile text să aibă un prefix care să indice sarcina în cauză, cum ar fi `translate: din engleză în franceză:`.

> [!WARNING]
> ⚠️ Nu acordăm atenție attention maskului a targeturilor, deoarece modelul nu se va aștepta la aceasta. În schimb, labelurile corespunzătoare unui padding token trebuie setate la `-100`, astfel încât acestea să fie ignorate în calculul pierderilor. Acest lucru va fi făcut mai târziu de data collatorul nostru, deoarece aplicăm padding dinamic, dar dacă utilizați padding aici, ar trebui să adaptați funcția de preprocesare pentru a seta toate labelurile care corespund simbolului de padding la `-100`.

Acum putem aplica această preprocesare dintr-o singură dată pe toate diviziunile datasetului nostru:

```py
tokenized_datasets = split_datasets.map(
    preprocess_function,
    batched=True,
    remove_columns=split_datasets["train"].column_names,
)
```

Acum că datele au fost preprocesate, suntem pregătiți să facem fine-tune modelul nostru preantrenat!

{#if fw === 'pt'}

## Fine-tuningul modelului cu API-ul `Trainer`[[fine-tuning-the-model-with-the-trainer-api]]

Codul real care utilizează `Trainer` va fi același ca înainte, cu o singură mică schimbare: folosim aici un [`Seq2SeqTrainer`](https://huggingface.co/transformers/main_classes/trainer.html#seq2seqtrainer), care este o subclasă a `Trainer` care ne va permite să ne ocupăm în mod corespunzător de evaluare, folosind metoda `generate()` pentru a prezice rezultatele din inputuri. Vom analiza acest aspect mai în detaliu atunci când vom vorbi despre calculul metricelor.

În primul rând, avem nevoie de un model pe care să îl aplicăm fine-tuningul. Vom utiliza API-ul obișnuit `AutoModel`:

```py
from transformers import AutoModelForSeq2SeqLM

model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

{:else}

## Fine-tuningul modelului cu Keras[[fine-tuning-the-model-with-keras]]

În primul rând, avem nevoie de un model pe care să îl aplicăm fine-tuningul. Vom folosi API-ul obișnuit `AutoModel`:

```py
from transformers import TFAutoModelForSeq2SeqLM

model = TFAutoModelForSeq2SeqLM.from_pretrained(model_checkpoint, from_pt=True)
```

<Tip warning={false}>

💡 Checkpointul `Helsinki-NLP/opus-mt-en-fr` are doar weighturi PyTorch, deci
veți primi o eroare dacă încercați să încărcați modelul fără a utiliza argumentul
`from_pt=True` în metoda `from_pretrained()`. Atunci când specificați
`from_pt=True`, biblioteca va descărca și va converti automat
weighturile PyTorch pentru voi. După cum puteți vedea, este foarte simplu să vă schimbați între
frameworkuri în 🤗 Transformers!

</Tip>

{/if}

Rețineți că de data aceasta folosim un model care a fost antrenat pe o sarcină de traducere și care poate fi utilizat deja, astfel încât nu există niciun avertisment cu privire la weighturile lipsă sau la cele nou inițializate.

### Data collation[[data-collation]]

Vom avea nevoie de un data collator care să se ocupe de paddingul pentru batching-ul dinamic. Nu putem folosi un `DataCollatorWithPadding` ca în [Capitolul 3](/course/chapter3) în acest caz, pentru că acesta doar face padding inputurilor (input Is, attention mask și token type IDs). Labelurile noastre ar trebui, de asemenea, să fie padded la lungimea maximă întâlnită în labeluri. Și, așa cum am menționat anterior, valoarea de padding utilizată pentru a face padding labelurilor ar trebui să fie `-100` și nu padding tokenul tokenizerului, pentru a ne asigura că aceste valori de padding sunt ignorate în calculul pierderilor.

Toate acestea sunt realizate de un [`DataCollatorForSeq2Seq`](https://huggingface.co/transformers/main_classes/data_collator.html#datacollatorforseq2seq). Ca și `DataCollatorWithPadding`, acesta preia `tokenizer` utilizat pentru preprocesarea inputurilor, dar preia și `modelul`. Acest lucru se datorează faptului că acest data collator va fi, de asemenea, responsabil de pregătirea ID-urilor de input ale decoderului, care sunt versiuni schimbate ale labelurilor cu un token special la început. Deoarece această schimbare se face ușor diferit pentru diferite arhitecturi, `DataCollatorForSeq2Seq` trebuie să cunoască obiectul `model`:

{#if fw === 'pt'}

```py
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model)
```

{:else}

```py
from transformers import DataCollatorForSeq2Seq

data_collator = DataCollatorForSeq2Seq(tokenizer, model=model, return_tensors="tf")
```

{/if}

Pentru a testa acest lucru pe câteva sampleuri, îl apelăm doar pe o listă de sampleuri din setul nostru de antrenare tokenizat:

```py
batch = data_collator([tokenized_datasets["train"][i] for i in range(1, 3)])
batch.keys()
```

```python out
dict_keys(['attention_mask', 'input_ids', 'labels', 'decoder_input_ids'])
```

Putem verifica dacă labelurile noastre au fost padded la lungimea maximă a batchului, folosind `-100`:

```py
batch["labels"]
```

```python out
tensor([[  577,  5891,     2,  3184,    16,  2542,     5,  1710,     0,  -100,
          -100,  -100,  -100,  -100,  -100,  -100],
        [ 1211,     3,    49,  9409,  1211,     3, 29140,   817,  3124,   817,
           550,  7032,  5821,  7907, 12649,     0]])
```

De asemenea, putem arunca o privire la ID-urile de input ale decoderului, pentru a vedea că acestea sunt versiuni schimbate ale labelurilor:

```py
batch["decoder_input_ids"]
```

```python out
tensor([[59513,   577,  5891,     2,  3184,    16,  2542,     5,  1710,     0,
         59513, 59513, 59513, 59513, 59513, 59513],
        [59513,  1211,     3,    49,  9409,  1211,     3, 29140,   817,  3124,
           817,   550,  7032,  5821,  7907, 12649]])
```

Iată labelurile pentru primul și al doilea element din datasetul nostru:

```py
for i in range(1, 3):
    print(tokenized_datasets["train"][i]["labels"])
```

```python out
[577, 5891, 2, 3184, 16, 2542, 5, 1710, 0]
[1211, 3, 49, 9409, 1211, 3, 29140, 817, 3124, 817, 550, 7032, 5821, 7907, 12649, 0]
```

{#if fw === 'pt'}

Vom transmite acest `data_collator` către `Seq2SeqTrainer`. În continuare, să aruncăm o privire la metrice.

{:else}

Acum putem folosi acest `data_collator` pentru a converti fiecare dintre dataseturile într-un `tf.data.Dataset`, gata pentru antrenare:

```python
tf_train_dataset = model.prepare_tf_dataset(
    tokenized_datasets["train"],
    collate_fn=data_collator,
    shuffle=True,
    batch_size=32,
)
tf_eval_dataset = model.prepare_tf_dataset(
    tokenized_datasets["validation"],
    collate_fn=data_collator,
    shuffle=False,
    batch_size=16,
)
```

{/if}


### Metrice[[metrics]]

<Youtube id="M05L1DhFqcw"/>

{#if fw === 'pt'}

Caracteristica pe care `Seq2SeqTrainer` o adaugă superclasei sale `Trainer` este capacitatea de a utiliza metoda `generate()` în timpul evaluării sau predicției. În timpul antrenării, modelul va utiliza `decoder_input_ids` cu un attention mask care asigură că nu utilizează tokenii de după tokenul pe care încearcă să îl prezică, pentru a accelera antrenarea. În timpul inferenței, nu le vom putea utiliza deoarece nu vom avea labeluri, deci este o idee bună să ne evaluăm modelul cu aceeași configurație.

După cum am văzut în [Capitolul 1](/course/chapter1/6), decoderul realizează inferența prin prezicerea tokenilor unul câte unul - lucru care este implementat behind the scenes în 🤗 Transformers prin metoda `generate()`. `Seq2SeqTrainer` ne va permite să folosim această metodă pentru evaluare dacă setăm `predict_with_generate=True`.

{/if}

Metricele tradiționale utilizate pentru traducere sunt [scorul BLEU](https://en.wikipedia.org/wiki/BLEU), introdus în [un articol din 2002](https://aclanthology.org/P02-1040.pdf) de Kishore Papineni et al. Scorul BLEU evaluează cât de apropiate sunt traducerile de labelurile lor. Acesta nu măsoară inteligibilitatea sau corectitudinea gramaticală a rezultatelor generate de model, ci utilizează reguli statistice pentru a se asigura că toate cuvintele din rezultatele generate apar și în targets. În plus, există reguli care penalizează repetițiile acelorași cuvinte dacă acestea nu sunt repetate și în targets(pentru a evita ca modelul să producă propoziții de tipul `"the the the the the the"`) și să producă propoziții care sunt mai scurte decât cele din targets(pentru a evita ca modelul să producă propoziții de tipul `"the"`).

Un punct slab al BLEU este că se așteaptă ca textul să fie deja tokenizat, ceea ce face dificilă compararea scorurilor între modele care utilizează tokenizere diferite. În schimb, cea mai frecvent utilizată măsură pentru evaluarea comparativă a modelelor de traducere este [SacreBLEU](https://github.com/mjpost/sacrebleu), care abordează acest punct slab (și altele) prin standardizarea etapei de tokenizare. Pentru a utiliza această metrică, trebuie mai întâi să instalăm biblioteca SacreBLEU:

```py
!pip install sacrebleu
```

Îl putem încărca apoi prin `evaluate.load()` așa cum am făcut în [Capitolul 3](/course/chapter3):

```py
import evaluate

metric = evaluate.load("sacrebleu")
```

Această metrică va lua texte ca inputuri și targeturi. Este conceput pentru a lua mai multe obiective acceptabile, deoarece există adesea mai multe traduceri acceptabile ale aceleiași propoziții - datasetul pe care îl folosim oferă doar una, dar nu este neobișnuit în NLP să găsim dataseturi care oferă mai multe propoziții ca labeluri. Deci, predicțiile ar trebui să fie o listă de propoziții, dar referințele ar trebui să fie o listă de liste de propoziții.

Hai să încercăm acest exemplu:

```py
predictions = [
    "This plugin lets you translate web pages between several languages automatically."
]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 46.750469682990165,
 'counts': [11, 6, 4, 3],
 'totals': [12, 11, 10, 9],
 'precisions': [91.67, 54.54, 40.0, 33.33],
 'bp': 0.9200444146293233,
 'sys_len': 12,
 'ref_len': 13}
```

Se obține un scor BLEU de 46,75, ceea ce este destul de bine - ca referință, modelul original Transformer din lucrarea ["Attention Is All You Need"](https://arxiv.org/pdf/1706.03762.pdf) a obținut un scor BLEU de 41,8 la o sarcină similară de traducere între engleză și franceză! (Pentru mai multe informații despre parametrii individuali, precum `counts` și `bp`, consultați [repositoriul SacreBLEU](https://github.com/mjpost/sacrebleu/blob/078c440168c6adc89ba75fe6d63f0d922d42bcfe/sacrebleu/metrics/bleu.py#L74).) Pe de altă parte, dacă încercăm cu cele două tipuri de predicții proaste (multe repetări sau prea scurte) care rezultă adesea din modelele de traducere, vom obține scoruri BLEU destul de proaste:

```py
predictions = ["This This This This"]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 1.683602693167689,
 'counts': [1, 0, 0, 0],
 'totals': [4, 3, 2, 1],
 'precisions': [25.0, 16.67, 12.5, 12.5],
 'bp': 0.10539922456186433,
 'sys_len': 4,
 'ref_len': 13}
```

```py
predictions = ["This plugin"]
references = [
    [
        "This plugin allows you to automatically translate web pages between several languages."
    ]
]
metric.compute(predictions=predictions, references=references)
```

```python out
{'score': 0.0,
 'counts': [2, 1, 0, 0],
 'totals': [2, 1, 0, 0],
 'precisions': [100.0, 100.0, 0.0, 0.0],
 'bp': 0.004086771438464067,
 'sys_len': 2,
 'ref_len': 13}
```

Scorul poate varia de la 0 la 100, iar mai mare înseamnă un scor mai bun.

{#if fw === 'tf'}

Pentru a trece de la rezultatele modelului la texte pe care metricele le pot utiliza, vom utiliza metoda `tokenizer.batch_decode()`. Trebuie doar să curățăm toate `-100` din labeluri; tokenizerul va face automat același lucru pentru padding token. Să definim o funcție care să preia modelul nostru și un dataset și să calculeze metrici pe acesta. De asemenea, vom utiliza un truc care crește dramatic performanța - compilarea codului nostru de generare cu [XLA](https://www.tensorflow.org/xla), compilatorul accelerat de algebră liniară al TensorFlow. XLA aplică diverse optimizări graficului de calcul al modelului și are ca rezultat îmbunătățiri semnificative ale vitezei și utilizării memoriei. După cum se descrie în [blogul](https://huggingface.co/blog/tf-xla-generate) Hugging Face, XLA funcționează cel mai bine atunci când shaperulire noastre de input nu variază prea mult. Pentru a face față acestui lucru, vom aplica padding inputurilor cu multipli ai 128 și vom crea un nou dataset cu padding collatorul, iar apoi vom aplica decoratorul `@tf.function(jit_compile=True)` funcției noastre de generare, care marchează întreaga funcție pentru compilare cu XLA.

```py
import numpy as np
import tensorflow as tf
from tqdm import tqdm

generation_data_collator = DataCollatorForSeq2Seq(
    tokenizer, model=model, return_tensors="tf", pad_to_multiple_of=128
)

tf_generate_dataset = model.prepare_tf_dataset(
    tokenized_datasets["validation"],
    collate_fn=generation_data_collator,
    shuffle=False,
    batch_size=8,
)


@tf.function(jit_compile=True)
def generate_with_xla(batch):
    return model.generate(
        input_ids=batch["input_ids"],
        attention_mask=batch["attention_mask"],
        max_new_tokens=128,
    )


def compute_metrics():
    all_preds = []
    all_labels = []

    for batch, labels in tqdm(tf_generate_dataset):
        predictions = generate_with_xla(batch)
        decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
        labels = labels.numpy()
        labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
        decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
        decoded_preds = [pred.strip() for pred in decoded_preds]
        decoded_labels = [[label.strip()] for label in decoded_labels]
        all_preds.extend(decoded_preds)
        all_labels.extend(decoded_labels)

    result = metric.compute(predictions=all_preds, references=all_labels)
    return {"bleu": result["score"]}
```

{:else}

Pentru a trece de la rezultatele modelului la textele pe care metricele le pot utiliza, vom utiliza metoda `tokenizer.batch_decode()`. Trebuie doar să curățăm toate `-100` din labeluri(tokenizerul va face automat același lucru pentru padding token):

```py
import numpy as np


def compute_metrics(eval_preds):
    preds, labels = eval_preds
    # In case the model returns more than the prediction logits
    if isinstance(preds, tuple):
        preds = preds[0]

    decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)

    # Replace -100s in the labels as we can't decode them
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

    # Some simple post-processing
    decoded_preds = [pred.strip() for pred in decoded_preds]
    decoded_labels = [[label.strip()] for label in decoded_labels]

    result = metric.compute(predictions=decoded_preds, references=decoded_labels)
    return {"bleu": result["score"]}
```

{/if}

Acum că am făcut acest lucru, suntem gata să facem fine-tune modelului!


### Fine-tuningul modelului[[fine-tuning-the-model]]

Primul pas pe care trebuie să îl faceți este să vă conectați la Hugging Face, astfel încât să vă puteți încărca rezultatele în Model Hub. Există o funcție convenabilă care vă ajuta cu acest lucru într-un notebook:

```python
from huggingface_hub import notebook_login

notebook_login()
```

Aceasta va afișa un widget în care puteți introduce datele voastre de autentificare Hugging Face.

Dacă nu lucrați într-un notebook, tastați următoarea linie în terminal:

```bash
huggingface-cli login
```

{#if fw === 'tf'}

Înainte de a începe, să vedem ce fel de rezultate obținem de la modelul nostru fără niciun antrenament:

```py
print(compute_metrics())
```

```
{'bleu': 33.26983701454733}
```

Odată făcut acest lucru, putem pregăti tot ce avem nevoie pentru a compila și antrena modelul nostru. Observați utilizarea `tf.keras.mixed_precision.set_global_policy("mixed_float16")` -- aceasta îi va spune lui Keras să se antreneze folosind float16, ceea ce poate oferi o creștere semnificativă a vitezei pe GPU-urile care o acceptă (Nvidia 20xx/V100 sau mai noi).

```python
from transformers import create_optimizer
from transformers.keras_callbacks import PushToHubCallback
import tensorflow as tf

# Numărul etapelor de antrenare este numărul de sampleuri din dataset, împărțit la dimensiunea batchului, apoi înmulțit
# cu numărul total de epoci. Rețineți că datasetul tf_train_dataset de aici este un dataset tf.data.Dataset în batchuri,
# nu datasetul original Hugging Face, deci len() este deja num_samples // batch_size.
num_epochs = 3
num_train_steps = len(tf_train_dataset) * num_epochs

optimizer, schedule = create_optimizer(
    init_lr=5e-5,
    num_warmup_steps=0,
    num_train_steps=num_train_steps,
    weight_decay_rate=0.01,
)
model.compile(optimizer=optimizer)

# Antrenarea în float16 cu precizie mixtă
tf.keras.mixed_precision.set_global_policy("mixed_float16")
```

În continuare, definim un `PushToHubCallback` pentru a încărca modelul nostru în Hub în timpul antrenamentului, așa cum am văzut în [secțiunea 2]((/course/chapter7/2)), iar apoi pur și simplu facem fit modelului cu acel callback:

```python
from transformers.keras_callbacks import PushToHubCallback

callback = PushToHubCallback(
    output_dir="marian-finetuned-kde4-en-to-fr", tokenizer=tokenizer
)

model.fit(
    tf_train_dataset,
    validation_data=tf_eval_dataset,
    callbacks=[callback],
    epochs=num_epochs,
)
```

Rețineți că puteți specifica numele repositoriului către care doriți să faceți push cu argumentul `hub_model_id` (în special, va trebui să utilizați acest argument pentru a face push către o organizație). De exemplu, atunci când am trimis modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` la `Seq2SeqTrainingArguments`. În mod implicit, repositoriul utilizat va fi în namespaceul vostru și denumit după output directory-ul pe care l-ați stabilit, deci aici va fi `"sgugger/marian-finetuned-kde4-en-to-fr"` (care este modelul la care am făcut legătura la începutul acestei secțiuni).

> [!TIP]
> 💡 Dacă output directory-ul pe care îl utilizați există deja, acesta trebuie să fie o clonă locală a repositoriului către care doriți să faceți push. Dacă nu este, veți primi o eroare atunci când apelați `model.fit()` și va trebui să setați un nume nou.

În cele din urmă, hai să vedem cum arată metricele noastre acum că antrenarea s-a încheiat:

```py
print(compute_metrics())
```

```
{'bleu': 57.334066271545865}
```

În acest moment, puteți utiliza widgetul de inferență de pe Model Hub pentru a testa modelul și pentru a-l partaja cu prietenii voștrii. Ați făcut fine-tune cu succes unui model pentru o sarcină de traducere - felicitări!

{:else}

Odată făcut acest lucru, putem defini `Seq2SeqTrainingArguments`. Ca și pentru `Trainer`, folosim o subclasă a `TrainingArguments` care conține câteva câmpuri suplimentare:

```python
from transformers import Seq2SeqTrainingArguments

args = Seq2SeqTrainingArguments(
    f"marian-finetuned-kde4-en-to-fr",
    evaluation_strategy="no",
    save_strategy="epoch",
    learning_rate=2e-5,
    per_device_train_batch_size=32,
    per_device_eval_batch_size=64,
    weight_decay=0.01,
    save_total_limit=3,
    num_train_epochs=3,
    predict_with_generate=True,
    fp16=True,
    push_to_hub=True,
)
```

În afară de hiperparametrii obișnuiți (cum ar fi rata de învățare, numărul de epoci, dimensiunea batch-ului și o anumită scădere a weighturilor), aici sunt câteva schimbări în comparație cu ceea ce am văzut în secțiunile anterioare:

- Nu setăm nicio evaluare periodică, deoarece evaluarea durează; ne vom evalua modelul doar o dată înainte și după antrenare.
- Setăm `fp16=True`, care accelerează antrenarea pe GPU-urile moderne.
- Setăm `predict_with_generate=True`, așa cum am discutat mai sus.
- Utilizăm `push_to_hub=True` pentru a încărca modelul în Hub la sfârșitul fiecărei epoci.

Rețineți că puteți specifica numele complet al repositoriului către care doriți să faceți push cu argumentul `hub_model_id` (în special, va trebui să utilizați acest argument pentru a face push către o organizație). De exemplu, atunci când am trimis modelul către organizația [`huggingface-course`](https://huggingface.co/huggingface-course), am adăugat `hub_model_id="huggingface-course/marian-finetuned-kde4-en-to-fr"` la `Seq2SeqTrainingArguments`. În mod implicit, repositoriul utilizat va fi în namespaceul vostru și denumit după output directory-ul pe care l-ați stabilit, deci în cazul nostru va fi `"sgugger/marian-finetuned-kde4-en-to-fr"` (care este modelul la care am făcut legătura la începutul acestei secțiuni).

> [!TIP]
> 💡 Dacă output directory-ul pe care îl utilizați există deja, acesta trebuie să fie o clonă locală a repositoriului către care doriți să faceți push. În caz contrar, veți primi o eroare atunci când vă definiți `Seq2SeqTrainer` și va trebui să stabiliți un nume nou.

În final, transmitem totul către `Seq2SeqTrainer`:

```python
from transformers import Seq2SeqTrainer

trainer = Seq2SeqTrainer(
    model,
    args,
    train_dataset=tokenized_datasets["train"],
    eval_dataset=tokenized_datasets["validation"],
    data_collator=data_collator,
    tokenizer=tokenizer,
    compute_metrics=compute_metrics,
)
```

Înainte de antrenare, ne vom uita mai întâi la scorul obținut de modelul nostru, pentru a verifica dacă nu înrăutățim lucrurile prin fine-tuning. Această comandă va dura ceva timp, așa că puteți lua o cafea sau două în timp ce se execută:

```python
trainer.evaluate(max_length=max_length)
```

```python out
{'eval_loss': 1.6964408159255981,
 'eval_bleu': 39.26865061007616,
 'eval_runtime': 965.8884,
 'eval_samples_per_second': 21.76,
 'eval_steps_per_second': 0.341}
```

Un scor BLEU de 39 nu este prea rău, ceea ce reflectă faptul că modelul nostru este deja bun la traducerea propozițiilor din engleză în franceză.

Urmează antrenarea, care va necesita, de asemenea, puțin timp:

```python
trainer.train()
```

Rețineți că, în timpul antrenamentului, de fiecare dată când modelul este salvat (aici, la fiecare epocă), acesta este încărcat în Hub în fundal. În acest fel, veți putea să reluați antrenarea pe o altă mașină, dacă este necesar.

Odată ce formarea este terminată, evaluăm din nou modelul nostru - sperăm că vom vedea o îmbunătățire a scorului BLEU!

```py
trainer.evaluate(max_length=max_length)
```

```python out
{'eval_loss': 0.8558505773544312,
 'eval_bleu': 52.94161337775576,
 'eval_runtime': 714.2576,
 'eval_samples_per_second': 29.426,
 'eval_steps_per_second': 0.461,
 'epoch': 3.0}
```

Aceasta este o îmbunătățire de aproape 14 puncte, ceea ce este minunat.

În final, folosim metoda `push_to_hub()` pentru a ne asigura că încărcăm cea mai recentă versiune a modelului. De asemenea, `Trainer` redactează un model card cu toate rezultatele evaluării și o încarcă. Acest model card conține metadate care ajută Model Hub să aleagă widgetul pentru demonstrația de inferență. De obicei, nu este nevoie să se menționeze nimic, deoarece poate deduce widgetul potrivit din clasa modelului, dar în acest caz, aceeași clasă de model poate fi utilizată pentru toate tipurile de probleme de tip sequence-to-sequence, așa că specificăm că este un model de traducere:

```py
trainer.push_to_hub(tags="translation", commit_message="Training complete")
```

Această comandă returnează URL-ul comitului pe care tocmai l-am făcut, dacă doriți să îl inspectați:

```python out
'https://huggingface.co/sgugger/marian-finetuned-kde4-en-to-fr/commit/3601d621e3baae2bc63d3311452535f8f58f6ef3'
```

În această etapă, puteți utiliza widget-ul de inferență de pe Model Hub pentru a vă testa modelul și pentru a-l partaja cu prietenii. Ați făcut fine-tune cu succes un model pentru o sarcină de traducere - felicitări!

Dacă doriți să pătrundeți puțin mai adânc în bucla de antrenare, vă vom arăta acum să faceți același lucru folosind 🤗 Accelerate.

{/if}

{#if fw === 'pt'}

## O buclă de antrenare personalizată[[a-custom-training-loop]]

Să aruncăm acum o privire la bucla de antrenare completă, astfel încât să puteți personaliza cu ușurință părțile de care aveți nevoie. Va arăta foarte asemănător cu ceea ce am făcut în [secțiunea 2](/course/chapter7/2) și [capitolul 3](/course/chapter7/4).

### Pregătirea tuturor lucrulilor pentru antrenare[[preparing-everything-for-training]]

Ați văzut toate acestea de câteva ori până acum, așa că vom trece prin cod destul de repede. Mai întâi vom construi `DataLoader`s din dataseturile noastre, după ce vom seta dataseturile în formatul `"torch"` astfel încât să obținem tensori PyTorch:

```py
from torch.utils.data import DataLoader

tokenized_datasets.set_format("torch")
train_dataloader = DataLoader(
    tokenized_datasets["train"],
    shuffle=True,
    collate_fn=data_collator,
    batch_size=8,
)
eval_dataloader = DataLoader(
    tokenized_datasets["validation"], collate_fn=data_collator, batch_size=8
)
```

În continuare, reinițializăm modelul, pentru a ne asigura că nu continuăm fine-tuningul de dinainte, ci pornim din nou de la modelul preantrenat:

```py
model = AutoModelForSeq2SeqLM.from_pretrained(model_checkpoint)
```

Pe urmă vom avea nevoie de un optimizator:

```py
from transformers import AdamW

optimizer = AdamW(model.parameters(), lr=2e-5)
```

Odată ce avem toate aceste obiecte, le putem trimite la metoda `accelerator.prepare()`. Amintiți-vă că, dacă doriți să vă antrenați pe un TPU într-un notebook Colab, va trebui să mutați tot acest cod într-o funcție de antrenare, care nu ar trebui să execute nicio celulă care inițializează un `Accelerator`.

```py
from accelerate import Accelerator

accelerator = Accelerator()
model, optimizer, train_dataloader, eval_dataloader = accelerator.prepare(
    model, optimizer, train_dataloader, eval_dataloader
)
```

Acum că am trimis `train_dataloader` la `accelerator.prepare()`, putem utiliza lungimea acestuia pentru a calcula numărul de pași de antrenare. Amintiți-vă că trebuie să facem acest lucru întotdeauna după pregătirea data loaderului, deoarece metoda respectivă va modifica lungimea `DataLoader`. Utilizăm un program liniar clasic de la rata de învățare la 0:

```py
from transformers import get_scheduler

num_train_epochs = 3
num_update_steps_per_epoch = len(train_dataloader)
num_training_steps = num_train_epochs * num_update_steps_per_epoch

lr_scheduler = get_scheduler(
    "linear",
    optimizer=optimizer,
    num_warmup_steps=0,
    num_training_steps=num_training_steps,
)
```

În cele din urmă, pentru a trimite modelul nostru către Hub, va trebui să creăm un obiect `Repository` într-un folder de lucru. În primul rând, conectați-vă la Hugging Face Hub, dacă nu sunteți deja conectat. Vom determina numele repositoriului pornind de la ID-ul modelului pe care dorim să îl atribuim modelului nostru (nu ezitați să înlocuiți `repo_name` cu propria alegere; acesta trebuie doar să conțină numele vostru de utilizator, ceea ce face funcția `get_full_repo_name()`):

```py
from huggingface_hub import Repository, get_full_repo_name

model_name = "marian-finetuned-kde4-en-to-fr-accelerate"
repo_name = get_full_repo_name(model_name)
repo_name
```

```python out
'sgugger/marian-finetuned-kde4-en-to-fr-accelerate'
```

Apoi putem clona acel repositoriu într-un folder local. Dacă există deja, acest folder local ar trebui să fie o clonă a repositoriului cu care lucrăm:

```py
output_dir = "marian-finetuned-kde4-en-to-fr-accelerate"
repo = Repository(output_dir, clone_from=repo_name)
```

Acum putem încărca orice salvăm în `output_dir` prin apelarea metodei `repo.push_to_hub()`. Acest lucru ne va ajuta să încărcăm modelele intermediare la sfârșitul fiecărei epoci.

### Bucla de antrenare[[training-loop]]

Acum suntem pregătiți să scriem bucla de antrenare completă. Pentru a simplifica partea de evaluare, definim această funcție `postprocess()` care preia predicțiile și labelurile și le convertește în liste de stringuri pe care obiectul nostru `metric` le va aștepta:

```py
def postprocess(predictions, labels):
    predictions = predictions.cpu().numpy()
    labels = labels.cpu().numpy()

    decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)

    # Replace -100 in the labels as we can't decode them.
    labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
    decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)

    # Some simple post-processing
    decoded_preds = [pred.strip() for pred in decoded_preds]
    decoded_labels = [[label.strip()] for label in decoded_labels]
    return decoded_preds, decoded_labels
```

Bucla de antrenare seamănă foarte mult cu cele din [secțiunea 2](/course/chapter7/2) și [capitolul 3](/course/chapter3), cu câteva diferențe în partea de evaluare - așa că hai să ne concentrăm pe asta!

Primul lucru pe care îl putem remarca este că folosim metoda `generate()` pentru a calcula predicțiile, dar aceasta este o metodă a modelul nostru de bază, nu pe modelul wrapped 🤗 Accelerate creat în metoda `prepare()`. De aceea, mai întâi facem unwrap modelului, apoi apelăm această metodă.

Al doilea lucru este că, la fel ca în cazul [token classification](/course/chapter7/2), este posibil ca două procese să fi făcut padding inputurilor și labelurilor cu forme diferite, așa că folosim `accelerator.pad_across_processes()` pentru a face predicțiile și labelurile de aceeași formă înainte de a apela metoda `gather()`. Dacă nu facem acest lucru, evaluarea va da o eroare sau se va bloca pentru totdeauna.

```py
from tqdm.auto import tqdm
import torch

progress_bar = tqdm(range(num_training_steps))

for epoch in range(num_train_epochs):
    # Training
    model.train()
    for batch in train_dataloader:
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)

        optimizer.step()
        lr_scheduler.step()
        optimizer.zero_grad()
        progress_bar.update(1)

    # Evaluation
    model.eval()
    for batch in tqdm(eval_dataloader):
        with torch.no_grad():
            generated_tokens = accelerator.unwrap_model(model).generate(
                batch["input_ids"],
                attention_mask=batch["attention_mask"],
                max_length=128,
            )
        labels = batch["labels"]

        # Necessary to pad predictions and labels for being gathered
        generated_tokens = accelerator.pad_across_processes(
            generated_tokens, dim=1, pad_index=tokenizer.pad_token_id
        )
        labels = accelerator.pad_across_processes(labels, dim=1, pad_index=-100)

        predictions_gathered = accelerator.gather(generated_tokens)
        labels_gathered = accelerator.gather(labels)

        decoded_preds, decoded_labels = postprocess(predictions_gathered, labels_gathered)
        metric.add_batch(predictions=decoded_preds, references=decoded_labels)

    results = metric.compute()
    print(f"epoch {epoch}, BLEU score: {results['score']:.2f}")

    # Save and upload
    accelerator.wait_for_everyone()
    unwrapped_model = accelerator.unwrap_model(model)
    unwrapped_model.save_pretrained(output_dir, save_function=accelerator.save)
    if accelerator.is_main_process:
        tokenizer.save_pretrained(output_dir)
        repo.push_to_hub(
            commit_message=f"Training in progress epoch {epoch}", blocking=False
        )
```

```python out
epoch 0, BLEU score: 53.47
epoch 1, BLEU score: 54.24
epoch 2, BLEU score: 54.44
```

Odată făcut acest lucru, ar trebui să aveți un model care are rezultate destul de asemănătoare cu cel antrenat cu `Seq2SeqTrainer`. Îl puteți verifica pe cel pe care l-am antrenat folosind acest cod la [*huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate*] (https://huggingface.co/huggingface-course/marian-finetuned-kde4-en-to-fr-accelerate). Și dacă doriți să testați orice modificări ale buclei de antrenare, le puteți implementa direct prin editarea codului prezentat mai sus!

{/if}

## Utilizarea modelului fine-tuned[[using-the-fine-tuned-model]]

V-am arătat deja cum puteți utiliza modelul pe căruia i-am aplicat fine-tune pe Model Hub cu widgetul de inferență. Pentru a-l utiliza la nivel local într-un `pipeline`, trebuie doar să specificăm identificatorul de model corespunzător:

```py
from transformers import pipeline

# Replace this with your own checkpoint
model_checkpoint = "huggingface-course/marian-finetuned-kde4-en-to-fr"
translator = pipeline("translation", model=model_checkpoint)
translator("Default to expanded threads")
```

```python out
[{'translation_text': 'Par défaut, développer les fils de discussion'}]
```

Așa cum era de așteptat, modelul nostru preantrenat și-a adaptat cunoștințele la corpusul pe care i-am făcut fine-tune și, în loc să lase cuvântul englezesc "threads", acum îl traduce în versiunea oficială franceză. Același lucru este valabil și pentru "plugin":

```py
translator(
    "Unable to import %1 using the OFX importer plugin. This file is not the correct format."
)
```

```python out
[{'translation_text': "Impossible d'importer %1 en utilisant le module externe d'importation OFX. Ce fichier n'est pas le bon format."}]
```

Un alt exemplu excelent de adaptare a domeniului!

> [!TIP]
> ✏️ **E rândul tău!** Care este rezultatul modelului pentru sampleul cuvântul "email" pe care l-ai identificat mai devreme?
